﻿ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Saxon.Api;
using WMM.Tapioca.Linkbase;
using WMM.Tapioca.Formula.Filters;
using WMM.Tapioca.Instance;
using WMM.Tapioca.Taxonomy;
using System.IO;
using WMM.Tapioca.Formula.Functions;
using System.Xml;
using System.Collections;
using System.Xml.Linq;
using WMM.Tapioca.Utilities;

namespace WMM.Tapioca.Formula
{
    /// <summary>
    /// The base class of all filters.
    /// </summary>
    public abstract class Filter : FormulaResource
    {
        protected static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        protected XNamespace xbrli = Constants.XBRLI;

        protected DTS dts;

        protected Dictionary<string, string> namespaces = new Dictionary<string, string>();

        private bool complement;

        /// <returns> Filters may be marked as complement="true",
        /// meaning that their boolean result is inverted.  </returns>
        public bool Complement
        {
            get { return complement; }
            set { complement = value; }
        }

        private bool cover;

        /// <returns>Filters may be marked as cover="false", which then excludes that covered aspect
        /// from implicit filtering of the related fact variable or matching by a relative filter.</returns>
        public bool Cover
        {
            get { return cover; }
            set { cover = value; }
        }        

        public Filter()
        {
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public Filter(Resource resource) :base(resource)
        {
            
        }


        // Factory method
        internal static Filter CreateFilter(Arc variableFilterArc, Resource filterResource, VariableSet variableSet)
        {
            Filter filter = null;

			// Tuple Filters

            if (filterResource.LocalName.Equals("parentFilter"))
            {
                filter = new ParentFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "parentFilter";                    
            }

            if (filterResource.LocalName.Equals("ancestorFilter"))
            {
                filter = new AncestorFilter(filterResource); 
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "ancestorFilter";                    
            }

            if (filterResource.LocalName.Equals("siblingFilter"))
            {
                filter = new SiblingFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "siblingFilter";                    
            }

            if (filterResource.LocalName.Equals("locationFilter"))
            {
                filter = new LocationFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "locationFilter";                    
            }

            // Concept Filter

            if (filterResource.LocalName.Equals("conceptName"))
            {
                filter = new ConceptNameFilter(filterResource, variableSet);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptName";                    
            }

            if (filterResource.LocalName.Equals("conceptBalance"))
            {
                filter = new ConceptBalanceFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptBalance";                    
            }

            if (filterResource.LocalName.Equals("conceptPeriodType"))
            {
                filter = new ConceptPeriodTypeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptPeriodType";                    
            }

            if (filterResource.LocalName.Equals("conceptCustomAttribute"))
            {
                filter = new ConceptCustomAttributeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptCustomAttribute";                    
            }

            if (filterResource.LocalName.Equals("conceptDataType"))
            {
                filter = new ConceptDataTypeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptDataType";                    
            }

            if (filterResource.LocalName.Equals("conceptSubstitutionGroup"))
            {
                filter = new ConceptSubstitutionGroupFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptSubstitutionGroup";                    
            }

            // Dimension Filter

            if (filterResource.LocalName.Equals("explicitDimension"))
            {
                filter = new ExplicitDimensionFilter(filterResource, variableSet);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "explicitDimension";                    
            }

            if (filterResource.LocalName.Equals("typedDimension"))
            {
                filter = new TypedDimensionFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "typedDimension";                    
            }

            // Period Filter

            if (filterResource.LocalName.Equals("period"))
            {
                filter = new Period(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "period";                    
            }

            if (filterResource.LocalName.Equals("periodStart"))
            {
                filter = new PeriodStartFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "periodStart";                    
            }

            if (filterResource.LocalName.Equals("periodEnd"))
            {
                filter = new PeriodEndFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "periodEnd";                    
            }

            if (filterResource.LocalName.Equals("periodInstant"))
            {
                filter = new PeriodInstantFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "periodInstant";                    
            }

            if (filterResource.LocalName.Equals("forever"))
            {
                filter = new ForeverFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "forever";                    
            }

            if (filterResource.LocalName.Equals("instantDuration"))
            {
                filter = new InstantDurationFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "instantDuration";                    
            }

            // Unit Filter

            if (filterResource.LocalName.Equals("generalMeasures"))
            {
                filter = new GeneralMeasuresFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "generalMeasures";                    
            }

            if (filterResource.LocalName.Equals("singleMeasure"))
            {
                filter = new SingleMeasureFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "singleMeasure";
            }

            // identifier filter

            if (filterResource.LocalName.Equals("identifier"))
            {
                filter = new IdentifierFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "identifier";                    
            }

            if (filterResource.LocalName.Equals("specificScheme"))
            {
                filter = new SpecificSchemeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "specificScheme";                    
            }

            if (filterResource.LocalName.Equals("regexpScheme"))
            {
                filter = new RegExpSchemeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "regexpScheme";                    
            }

            if (filterResource.LocalName.Equals("specificIdentifier"))
            {
                filter = new SpecificIdentifierFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "specificIdentifier";
			}                    

            if (filterResource.LocalName.Equals("regexpIdentifier"))
            {
                filter = new RegExpIdentifierFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "regexpIdentifier";                    
            }

            // General Filter

            if (filterResource.LocalName.Equals("general"))
            {
                filter = new GeneralFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "general";                    
            }

            // Segment and Scenario

            if (filterResource.LocalName.Equals("segment"))
            {
                filter = new SegmentFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "segment";                    
            }

            if (filterResource.LocalName.Equals("scenario"))
            {
                filter = new ScenarioFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "scenario";                    
            }

            // Value Filter

            if (filterResource.LocalName.Equals("nil"))
            {
                filter = new NilFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "nil";                    
            }

            if (filterResource.LocalName.Equals("precision"))
            {
                filter = new PrecisionFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "precision";                    
            }

            // Boolean Filter

            if (filterResource.LocalName.Equals("andFilter"))
            {
                filter = new AndFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "andFilter";

                (filter as AndFilter).SubFilters.AddRange(GetSubFilters(filterResource.Label, variableFilterArc.ExtendedLinkParent, variableSet));                   
            }

            if (filterResource.LocalName.Equals("orFilter"))
            {
                filter = new OrFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "orFilter";

                (filter as OrFilter).SubFilters.AddRange(GetSubFilters(filterResource.Label, variableFilterArc.ExtendedLinkParent, variableSet));     
            }

            // Concept Relationship Filter

            if (filterResource.LocalName.Equals("conceptRelation"))
            {
                filter = new ConceptRelationFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "conceptRelation";                    
            }
            
            // Match Filter

            if (filterResource.LocalName.Equals("matchConcept"))
            {
                filter = new MatchConceptFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchConcept";                    
            }

            if (filterResource.LocalName.Equals("matchLocation"))
            {
                filter = new MatchLocationFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchLocation";                    
            }

            if (filterResource.LocalName.Equals("matchUnit"))
            {
                filter = new MatchUnitFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchUnit";                    
            }

            if (filterResource.LocalName.Equals("matchEntityIdentifier"))
            {
                filter = new MatchEntityIdentifierFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchEntityIdentifier";                    
            }

            if (filterResource.LocalName.Equals("matchPeriod"))
            {
                filter = new MatchPeriodFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchPeriod";                    
            }

            if (filterResource.LocalName.Equals("matchSegment"))
            {
                filter = new MatchSegmentFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchSegment";                    
            }

            if (filterResource.LocalName.Equals("matchScenario"))
            {
                filter = new MatchScenarioFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchScenario";                    
            }

            if (filterResource.LocalName.Equals("matchNonXDTSegment"))
            {
                filter = new MatchNonXDTSegmentFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchNonXDTSegment";                    
            }

            if (filterResource.LocalName.Equals("matchNonXDTScenario"))
            {
                filter = new MatchNonXDTScenarioFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "matchNonXDTScenario";                    
            }

            if (filterResource.LocalName.Equals("matchDimension"))
            {
                filter = new MatchDimensionFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);
                filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                filter.ResourceName = "matchDimension";                    
            }

            // Relative Filter

            if (filterResource.LocalName.Equals("relativeFilter"))
            {
                filter = new RelativeFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "relativeFilter";                    
            }

            // Aspect Cover

            if (filterResource.LocalName.Equals("aspectCover"))
            {
                filter = new AspectCoverFilter(filterResource);
                filter.Complement = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("complement") ?? string.Empty);

                if (variableFilterArc.ArcElement.Attribute("cover") != null)
                {
                    filter.Cover = Boolean.Parse((string)variableFilterArc.ArcElement.Attribute("cover") ?? string.Empty);
                }

                filter.ResourceName = "aspectCover";
            }

            return filter;
        }


        protected static List<Filter> GetSubFilters(string booleanFilterLabel, ExtendedLink extendedLink, VariableSet variableSet)
        {
            List<Filter> subFilters = new List<Filter>();

            foreach (Arc arc in extendedLink.Arcs)
            {
                if (arc.ArcRole.AbsoluteUri.Equals("http://xbrl.org/arcrole/2008/boolean-filter") && arc.FromId == booleanFilterLabel)
                {
                    Resource filterResource = GetResource(arc.ToId, arc.ExtendedLinkParent);
                    subFilters.Add(CreateFilter(arc, filterResource, variableSet));
                }
            }

            return subFilters;
        }


        protected static Resource GetResource(string resourceLabel, ExtendedLink extendedLinkParent)
        {
            foreach (Linkbase.Resource resource in extendedLinkParent.Resources)
            {
                if (resource.Label.Equals(resourceLabel))
                {
                    return resource;
                }
            }

            return null;
        }

        public abstract List<FactItem> EvaluateFilter(VariableSet variableSet, List<FactItem> itemList, DTS dts);


        protected string EvaluateExpression(string expression)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();

            XPathSelector selector = compiler.Compile(expression).Load();

            //SetContextItem(processor, selector, dts.Instance.ThisXDocument.Root.GetXmlNode());

            string result = selector.Evaluate().ToString();

            return result;
        }


        protected string EvaluateExpression(List<string> dependecyVariables, VariableSet variableSet, string expression)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();

            DeclareVariables2(dependecyVariables, variableSet, compiler);

            XPathSelector selector = compiler.Compile(expression).Load();

            SetContextItem(processor, selector, variableSet.dts.Instance.ThisXDocument.Root.GetXmlNode());

            variableSet.BindParameters(selector, variableSet, null);

            BindGeneralVariable(variableSet, compiler, selector);

            foreach (string variableName in dependecyVariables)
            {
                List<FactItem> factItems = variableSet.variableItems[variableName];

                foreach (FactItem item in factItems)
                {
                    BindVariableValue(processor, variableSet, selector, variableName, item);                    

                    return selector.Evaluate().ToString();
                }
            }
            
            string result = selector.Evaluate().ToString();

            return result;
        }
        


        protected void BindGeneralVariable(VariableSet variableSetType, XPathCompiler compiler, XPathSelector selector)
        {
            foreach (VariableSet variableSet in variableSetType.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("generalVariable"))
                {
                    GeneralVariable generalVariable = GetGeneralVariable(variableSetType, variableSet);

                    string prefix = variableSet.Name.GetPrefix();

                    XPathSelector selector2 = compiler.Compile(generalVariable.Select).Load();

                    variableSetType.BindParameters(selector2, variableSetType, null);

                    if (prefix.Length > 0)
                    {
                        selector2.SetVariable(new QName(namespaces[prefix], variableSet.Name), new XdmAtomicValue(0));
                    }
                    else
                    {
                        selector2.SetVariable(new QName(variableSet.Name), new XdmAtomicValue(0));
                    }

                    XdmValue xdmValue = selector2.Evaluate();

                    if (prefix.Length > 0)
                    {
                        selector.SetVariable(new QName(namespaces[prefix], variableSet.Name), xdmValue);
                    }
                    else
                    {
                        selector.SetVariable(new QName(variableSet.Name), xdmValue);
                    }
                }
            }
        }


        internal string EvaluateXPathExpression(string expression, XmlNode contextItem)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();
            compiler.DeclareNamespace("xfi", "http://www.xbrl.org/2008/function/instance");

            if (dts.Instance != null)
            {
                compiler.BaseUri = GetPath(dts.Instance.ThisXDocument.BaseUri);
            }

            XPathSelector selector = compiler.Compile(expression).Load();

            SetContextItem(processor, selector, contextItem);

            string result = selector.Evaluate().ToString();

            return result;
        }


        protected string EvaluateXPathExpression(VariableSet variableSet, string expression, XmlNode contextItem)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();

            compiler.DeclareNamespace("xfi", "http://www.xbrl.org/2008/function/instance");

            foreach (string prefix in variableSet.namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, variableSet.namespaces[prefix]);
            }

            if (dts.Instance != null)
            {
                compiler.BaseUri = GetPath(dts.Instance.ThisXDocument.BaseUri);
            }

            XPathSelector selector = compiler.Compile(expression).Load();

            SetContextItem(processor, selector, contextItem);

            string result = selector.Evaluate().ToString();

            return result;
        }


        protected string EvaluateXPathExpression(VariableSet variableSet, string expression, FactItem item)
        {
            // Create a Processor instance.
            Processor processor = new Processor();

            RegisterExtensionFunctions(processor);

            // Create the XPath expression.
            XPathCompiler compiler = processor.NewXPathCompiler();
            //compiler.DeclareNamespace("xfi", "http://www.xbrl.org/2008/function/instance");

            DeclareVariables(variableSet, compiler);

            if (dts.Instance != null)
            {
                compiler.BaseUri = GetPath(dts.Instance.ThisXDocument.BaseUri);
            }

            XPathSelector selector = compiler.Compile(expression).Load();

            XNamespace ns = item.Namespace;

            foreach (VariableSet variable in variableSet.VariableSetArcs)
            {
                if (variable.Variable.Name.LocalName.Equals("factVariable"))
                {
                    XmlNode xmlNode = item.FactElement.GetXmlNode();
                    XdmNode node = processor.NewDocumentBuilder().Build(xmlNode);
                    IEnumerator ee = node.EnumerateAxis(XdmAxis.Child);

                    XdmNode n = (XdmNode)ee.Current;

                    string prefix = variable.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        selector.SetVariable(new QName(variableSet.namespaces[prefix], variable.Name), n);
                    }
                    else
                    {
                        selector.SetVariable(new QName(variable.Name), n);
                    }
                }
            }

            SetContextItem(processor, selector, dts.Instance.ThisXDocument.Root.GetXmlNode());

            Dictionary<string, string> parameterDictionary = new Dictionary<string, string>();

            variableSet.BindParameters(selector, variableSet, parameterDictionary);

            string result = selector.Evaluate().ToString();

            return result;
        }


        protected void DeclareVariables(VariableSet variableSetType, XPathCompiler compiler)
        {
            foreach (string prefix in variableSetType.namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, variableSetType.namespaces[prefix]);
            }

            foreach (VariableSet variableSet in variableSetType.VariableSetArcs)
            {
                if (variableSet.Variable.Name.LocalName.Equals("factVariable"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(variableSetType.namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }

                if (variableSet.Variable.Name.LocalName.Equals("generalVariable"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(variableSetType.namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }

                if (variableSet.Variable.Name.LocalName.Equals("parameter"))
                {
                    string prefix = variableSet.Name.GetPrefix();

                    if (prefix.Length > 0)
                    {
                        compiler.DeclareVariable(new QName(variableSetType.namespaces[prefix], variableSet.Name));
                    }
                    else
                    {
                        compiler.DeclareVariable(new QName(variableSet.Name));
                    }
                }
            }
        }


        protected void DeclareVariables2(List<string> dependecyVariables, VariableSet variableSet, XPathCompiler compiler)
        {
            foreach (string prefix in variableSet.namespaces.Keys)
            {
                compiler.DeclareNamespace(prefix, variableSet.namespaces[prefix]);
            }

            foreach (string variableName in dependecyVariables)
            {
                string prefix = variableName.GetPrefix();

                if (prefix.Length > 0)
                {
                    compiler.DeclareVariable(new QName(variableSet.namespaces[prefix], variableName));
                }
                else
                {
                    compiler.DeclareVariable(new QName(variableName));
                }             
            }
        }


        protected void BindVariableValue(Processor processor, VariableSet variableSet, XPathSelector selector, string variableName, FactItem item)
        {
            string prefix = variableName.GetPrefix();

            XdmNode xdmNode = processor.NewDocumentBuilder().Build(item.FactElement.ToXmlNode());

            IEnumerator enumerator = xdmNode.EnumerateAxis(XdmAxis.Child);

            XdmNode elementNode = (XdmNode)enumerator.Current;

            if (prefix.Length > 0)
            {
                selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), elementNode);
            }
            else
            {
                selector.SetVariable(new QName(variableName), elementNode);
            }

            //double value;

            //if (prefix.Length > 0)
            //{
            //    if (double.TryParse(item.Value, out value))
            //    {
            //        selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), new XdmAtomicValue(value));
            //    }
            //    else
            //    {
            //        selector.SetVariable(new QName(variableSet.namespaces[prefix], variableName), new XdmAtomicValue(item.Value));
            //    }
            //}
            //else
            //{
            //    if (double.TryParse(item.Value, out value))
            //    {
            //        selector.SetVariable(new QName(variableName), new XdmAtomicValue(value));
            //    }
            //    else
            //    {
            //        selector.SetVariable(new QName(variableName), new XdmAtomicValue(item.Value));
            //    }
            //}
        }


        protected static void RegisterExtensionFunctions(Processor processor)
        {
            processor.RegisterExtensionFunction(new PrecisionXfi());
            // ===================================== Contexts and units ==================================================
            processor.RegisterExtensionFunction(new ContextXfi());
            processor.RegisterExtensionFunction(new UnitXfi());
            processor.RegisterExtensionFunction(new UnitNumeratorXfi());
            processor.RegisterExtensionFunction(new UnitDenominatorXfi());
            processor.RegisterExtensionFunction(new MeasureNameXfi());
            processor.RegisterExtensionFunction(new PeriodXfi());
            processor.RegisterExtensionFunction(new ContextPeriodXfi());
            processor.RegisterExtensionFunction(new IsStartEndPeriodXfi());
            processor.RegisterExtensionFunction(new IsForeverPeriodXfi());
            processor.RegisterExtensionFunction(new IsDurationPeriodXfi());
            processor.RegisterExtensionFunction(new IsInstantPeriodXfi());
            processor.RegisterExtensionFunction(new PeriodStartXfi());
            processor.RegisterExtensionFunction(new PeriodEndXfi());
            processor.RegisterExtensionFunction(new PeriodInstantXfi());
            processor.RegisterExtensionFunction(new EntityXfi());
            processor.RegisterExtensionFunction(new ContextEntityXfi());
            processor.RegisterExtensionFunction(new IdentifierXfi());
            processor.RegisterExtensionFunction(new ContextIdentifierXfi());
            processor.RegisterExtensionFunction(new EntityIdentifierXfi());
            processor.RegisterExtensionFunction(new IdentifierValueXfi());
            processor.RegisterExtensionFunction(new IdentifierSchemeXfi());
            processor.RegisterExtensionFunction(new SegmentXfi());
            processor.RegisterExtensionFunction(new EntitySegmentXfi());
            processor.RegisterExtensionFunction(new ContextSegmentXfi());
            processor.RegisterExtensionFunction(new ScenarioXfi());
            processor.RegisterExtensionFunction(new ContextScenarioXfi());
            // =============================== Facts and tuples ===========================================
            processor.RegisterExtensionFunction(new ItemsInInstanceXfi());
            processor.RegisterExtensionFunction(new TuplesInInstanceXfi());
            processor.RegisterExtensionFunction(new ItemsInTupleXfi());
            processor.RegisterExtensionFunction(new TuplesInTupleXfi());
            // ======================================== Predicates ==================================================
            processor.RegisterExtensionFunction(new IdenticalNodesXfi());
            processor.RegisterExtensionFunction(new SEqualXfi());
            processor.RegisterExtensionFunction(new UEqualXfi());

            // ======================================== Other Functions ==================================================
            processor.RegisterExtensionFunction(new FactExplicitDimensionValueXfi());
            processor.RegisterExtensionFunction(new FactDimensionSequal2Xfi());
        }


        protected void SetContextItem(Processor processor, XPathSelector selector, XmlNode contextItem)
        {
            XdmNode indoc = null;

            if (dts.Instance != null)
            {
                indoc = processor.NewDocumentBuilder().Build(contextItem);
                IEnumerator e = indoc.EnumerateAxis(XdmAxis.Child);
                XdmNode n = (XdmNode)e.Current;
                selector.ContextItem = n;
            }
            else
            {
                indoc = processor.NewDocumentBuilder().Build(new XmlDocument().CreateElement("xbrl"));
                selector.ContextItem = indoc;
            }
        }


        protected List<FactItem> FilteringFacts(VariableSet variableSet, FactVariable factVariable, List<FactItem> itemsToFilter)
        {
            foreach (Filter filter in factVariable.Filters)
            {
                itemsToFilter = filter.EvaluateFilter(variableSet, itemsToFilter, variableSet.dts);
            }

            return itemsToFilter;
        }


        protected List<FactItem> GetItems(List<Fact> facts)
        {
            List<FactItem> items = new List<FactItem>();

            foreach (Fact fact in facts)
            {
                if (fact is FactItem)
                {
                    items.Add(fact as FactItem);
                }

                if (fact is FactTuple)
                {
                    items.AddRange(GetItems((fact as FactTuple).Facts));
                }
            }

            return items;
        }


        protected FactVariable GetFactVariable(string name, VariableSet assertionOrFormula)
        {
            foreach (VariableSet VariableSet in assertionOrFormula.VariableSetArcs)
            {
                if (name.Equals(VariableSet.Name))
                {
                    foreach (FactVariable factVariable in assertionOrFormula.FactVariables)
                    {
                        if (factVariable.Label.Equals(VariableSet.To))
                        {
                            return factVariable;
                        }
                    }
                }
            }

            return null;
        }


        protected FactVariable GetFactVariableFromFilter(string filterLabel, VariableSet assertionOrFormula)
        {            
            foreach (FactVariable factVariable in assertionOrFormula.FactVariables)
            {
                foreach (Arc arc in factVariable.VariableFilterArcs)
                {
                    if (arc.ToId.Equals(filterLabel))
                    {
                        foreach (VariableSet VariableSet in assertionOrFormula.VariableSetArcs)
                        {
                            if (arc.FromId.Equals(VariableSet.To))
                            {
                                return factVariable;
                            }
                        }
                    }
                }
            }
            
            return null;
        }


        protected string GetVariableName(string filterLabel, VariableSet assertionOrFormula)
        {
            foreach (FactVariable factVariable in assertionOrFormula.FactVariables)
            {
                foreach (Arc arc in factVariable.VariableFilterArcs)
                {
                    if (arc.ToId.Equals(filterLabel))
                    {
                        foreach (VariableSet variableSet in assertionOrFormula.VariableSetArcs)
                        {
                            if (arc.FromId.Equals(variableSet.To))
                            {
                                return variableSet.Name;
                            }
                        }
                    }
                }
            }

            return null;
        }


        protected Vertex GetVertexThisVariable(VariableSet variableSet, string thisVariableName)
        {
            foreach (Vertex vertex in variableSet.dependencyGraph.vertices())
            {
                if (vertex.name.Equals(thisVariableName))
                {
                    return vertex;
                }
            }

            return null;
        }


        internal GeneralVariable GetGeneralVariable(VariableSet variableSet, VariableSet variableSetArc)
        {
            foreach (GeneralVariable generalVariable in variableSet.GeneralVariables)
            {
                if (generalVariable.Label.Equals(variableSetArc.To))
                {
                    return generalVariable;
                }
            }

            return null;
        }


        protected Locator GetLocator(Arc arc, string label)
        {
            foreach (Locator locator in arc.ExtendedLinkParent.Locators)
            {
                if (label.Equals(locator.Label))
                {
                    return locator;
                }
            }

            return null;
        }


        protected bool IsAxisSatisfied(MemberModel memberModel, Locator locatorFrom, Concept conceptFrom, FactItem itemToFilter, string explicitMemberValue)
        {
            switch (memberModel.Axis)
            {
                case "child-or-self":
                    foreach (Arc arc in locatorFrom.OutArcs)
                    {
                        Concept conceptTo = dts.GetConceptByID(arc.ToLocator.HrefResourceId);

                        if (HasDomainValue(conceptFrom.Name, itemToFilter) || HasDomainValue(conceptTo.Name, itemToFilter))
                        {
                            return true;
                        }
                    }
                    break;
                case "child":

                    foreach (Arc arc in locatorFrom.OutArcs)
                    {
                        Concept conceptTo = dts.GetConceptByID(arc.ToLocator.HrefResourceId);

                        if (conceptTo.Name == explicitMemberValue.GetLocalName())
                        {
                            return true;
                        }
                    }
                    break;
                case "descendant-or-self":
                    List<string> descendantOrSelfConcepts = new List<string>();
                    descendantOrSelfConcepts = GetDescendants(locatorFrom);
                    descendantOrSelfConcepts.Add(conceptFrom.Name);

                    if (descendantOrSelfConcepts.Contains(explicitMemberValue.GetLocalName()))
                    {
                        return true;
                    }
                    break;
                case "descendant":
                    List<string> descendantConcepts = new List<string>();
                    descendantConcepts = GetDescendants(locatorFrom);

                    if (descendantConcepts.Contains(explicitMemberValue.GetLocalName()))
                    {
                        return true;
                    }
                    break;
            }
            return false;
        }


        protected List<string> GetDependecyVariables(VariableSet variableSet, string thisVariableName)
        {
            Vertex thisVertex = GetVertexThisVariable(variableSet, thisVariableName);

            List<string> dependecyVariables = new List<string>();

            if (thisVertex != null)
            {
                foreach (Edge edge in thisVertex.outEdges)
                {
                    dependecyVariables.Add(edge.destination.name);
                }
            }

            return dependecyVariables;
        }


        private bool HasDomainValue(string domainValue, FactItem itemToFilter)
        {
            IEnumerable<XElement> explicitDimensions = itemToFilter.ContextRef.ContextElement.Descendants().Where(e => e.Name.LocalName == "explicitMember");

            foreach (XElement explicitDimension in explicitDimensions)
            {
                if (explicitDimension.Value.GetLocalName() == domainValue)
                {
                    return true;
                }
            }

            return false;
        }


        private List<string> GetDescendants(Locator fromLocator)
        {
            List<string> temp = new List<string>();

            foreach (Arc arc in fromLocator.OutArcs)
            {
                Locator to = GetLocator(arc, arc.ToId);
                Concept conceptTo = dts.GetConceptByID(to.HrefResourceId);
                temp.Add(conceptTo.Name);

                temp.AddRange(this.GetDescendants(arc.ToLocator));
            }

            return temp;
        }


        protected string GetPath(string uriBase)
        {
            string path = uriBase;
            int LastPathSeparator = uriBase.LastIndexOf('/');
            if (LastPathSeparator == -1)
            {
                LastPathSeparator = uriBase.LastIndexOf('\\');
                if (LastPathSeparator != -1)
                {
                    path = uriBase.Substring(0, LastPathSeparator + 1);
                }
            }
            path = uriBase.Substring(0, LastPathSeparator + 1);
            return path;
        }
    }
}
